﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Xml;

namespace NHTrace
{
  /// <summary>
  /// Based on ReceiverUtils class from log2console project: http://log2console.codeplex.com/ - Copyright (c) 2007, Rémy Baudet
  /// </summary>
  public static class ReceiverUtils
  {
    static readonly DateTime s1970 = new DateTime(1970, 1, 1);

    /// <summary>
    /// Here we expect the log event to use the log4j schema.
    /// Sample:
    ///     <log4j:event logger="Statyk7.Another.Name.DummyManager" timestamp="1184286222308" level="ERROR" thread="1">
    ///         <log4j:message>This is an Message</log4j:message>
    ///         <log4j:properties>
    ///             <log4j:data name="log4jmachinename" value="remserver" />
    ///             <log4j:data name="log4net:HostName" value="remserver" />
    ///             <log4j:data name="log4net:UserName" value="REMSERVER\Statyk7" />
    ///             <log4j:data name="log4japp" value="Test.exe" />
    ///         </log4j:properties>
    ///     </log4j:event>
    /// </summary>
    /// 
    /// Implementation inspired from: http://geekswithblogs.net/kobush/archive/2006/04/20/75717.aspx
    /// 
    public static LogMessage ParseLog4JXmlLogEvent(string logEvent, string defaultLogger)
    {
      LogMessage logMsg = new LogMessage();

      try
      {
        NameTable nt = new NameTable();
        XmlNamespaceManager nsmanager = new XmlNamespaceManager(nt);
        nsmanager.AddNamespace("log4j", "http://jakarta.apache.org/log4j/");
        XmlParserContext context = new XmlParserContext(nt, nsmanager, "elem", XmlSpace.None, Encoding.ASCII);

        using (XmlTextReader reader = new XmlTextReader(logEvent, XmlNodeType.Element, context))
        {
          reader.Read();
          if ((reader.MoveToContent() != XmlNodeType.Element) || (reader.Name != "log4j:event"))
            throw new Exception("The Log Event is not a valid log4j Xml block.");
          long timeStamp;
          if (long.TryParse(reader.GetAttribute("timestamp"), out timeStamp))
            logMsg.TimeStamp = s1970.AddMilliseconds(timeStamp).ToLocalTime();
          int eventDepth = reader.Depth;
          reader.Read();
          while (reader.Depth > eventDepth)
          {
            if (reader.MoveToContent() == XmlNodeType.Element)
              switch (reader.Name)
              {
                case "log4j:message":
                  logMsg.Message = reader.ReadString();
                  break;
                case "log4j:throwable":
                  logMsg.Message += Environment.NewLine + reader.ReadString();
                  break;
                case "log4j:locationInfo":
                  break;
                case "log4j:properties":
                  reader.Read();
                  while (reader.MoveToContent() == XmlNodeType.Element && reader.Name == "log4j:data")
                  {
                    string name = reader.GetAttribute("name");
                    if (name.Equals("log4japp"))
                    {
                      string value = reader.GetAttribute("value");
                      logMsg.ApplicationName = value;
                    }
                    reader.Read();
                  }
                  break;
              }
            reader.Read();
          }
        }
      }
      catch (Exception ex)
      {
        logMsg.Message = String.Format("{0} - Original log message: {1}", ex.Message, logEvent);
        logMsg.TimeStamp = DateTime.Now;
      }

      return logMsg;
    }
  }
}